package com.xtm.exercise.base;

import org.apache.commons.io.FilenameUtils;
import org.junit.Test;

import java.io.*;
import java.util.Scanner;

/**
 * IO题库
 Files的常用方法都有哪些？

	 Files.exists()：检测文件路径是否存在。
	 Files.createFile()：创建文件。
	 Files.createDirectory()：创建文件夹。
	 Files.delete()：删除一个文件或目录。
	 Files.copy()：复制文件。
	 Files.move()：移动文件。
	 Files.size()：查看文件个数。
	 Files.read()：读取文件。
	 Files.write()：写入文件。

 字节流：
	 字节流：(可以操作任何类型的文件)
	 InputStream（抽象类）：
	 InputStream中的常用方法：
	 int read()：一次读取一个字节（返回值为字节本身）
	 int read(byte[] b)：一次读取一个字节数组（返回值为读取到的字节个数）
	 close():关闭流
	 InputStream的子类：
	 FileInputStream：
	 构造方法：
	 FileInputStream(File file)
	 FileInputStream(String name)
	 BufferedInputStream（高效字节流）：
	 构造方法：
	 BufferedInputStream(InputStream in) ：接收一个FileInputStream对象
	 特点：效率高速度快，适用于传输比较大的文件。


	 OutputStream（抽象类）：
	 OutputStream中的常用方法：
	 write(int b) ：将指定的字节写入输出流中
	 write(byte[] b, int off, int len) ：将指定的字节数组，从off索引开始的len个字节写入输出流中
	 close():关闭流
	 OutputStream的子类：
	 FileOutputStream：
	 构造方法：
	 FileOutputStream(File file)
	 FileOutputStream(String name)
	 FileOutputStream(String name, boolean append) ：后面添加一个boolean类型的值，赋值为true时可以实现追加写入的功能
	 BufferedInputStream（高效字节流）：
	 构造方法：
	 BufferedOutputStream(OutputStream out)：接收一个FileOutputStream对象
	 特点：效率高速度快，适用于传输比较大的文件。


 字符流
	 字符流：（只能操作文本文件，比如.txt)：
	 字符流的特点：
	 字符流 = 字节流+编码表
	 Reader（抽象类）：
	 Reader中常用的方法：
	 int read() ：一次读取一个字符（返回值是读取到的字符本身）
	 int read(char[] cbuf) ：一次读取一个字符数组（返回值是读取到的字符个数）
	 close()：关闭流
	 flush() ：刷新该流的缓冲
	 Reader的子类：
	 InputStreamReader（转换流）：
	 构造方法：
	 InputStreamReader(InputStream is):默认编码，GBK
	 InputStreamReader(InputStream is,String charsetName):指定编码
	 转换流的作用：
	 就是把字节流转换字符流来使用。
	 FileReader：
	 构造方法：
	 FileReader(String fileName)
	 FileReader(File file)
	 特点：默认使用GBK编码，只有一种编码方式，若想使用GBK以外的其他编码方式，只能用InputStreamReader
	 BufferedReader：
	 构造方法：
	 BufferedReader(Reader in) ：接收一个字符流对象（FileReader或者InputStreamReader）
	 特有的方法：
	 String readLine() ：一次读取一行数据
	 特点：效率高速度快，适用于传输比较大的文件。


	 Writer（抽象类）：
	 Writer中常用的方法：
	 write(int c) ：将指定的字符写入输出流中
	 write(char[] cbuf, int off, int len) ：将指定的字符数组，从off索引开始的len个字符写入到输出流中
	 write(String str)  ：将指定的字符串写入到输出流中
	 close()：关闭流
	 flush() ：刷新该流的缓冲
	 Writer的子类：
	 OutputStreamWriter（转换流）：
	 构造方法：
	 OutputStreamWriter(OutputStream os):默认编码，GBK
	 OutputStreamWriter(OutputStream os,String charsetName):指定编码。
	 转换流的作用：
	 就是把字节流转换字符流来使用。
	 FileWriter：
	 构造方法：
	 FileWriter(File file)
	 FileWriter(File file, boolean append) ：后面添加一个boolean类型的值，赋值为true时可以实现追加写入的功能
	 FileWriter(String fileName)
	 FileWriter(String fileName, boolean append) ：后面添加一个boolean类型的值，赋值为true时可以实现追加写入的功能
	 特点：默认使用GBK编码，只有一种编码方式，若想使用GBK以外的其他编码方式，只能用OutputStreamWriter
	 BufferedWriter：
	 构造方法：
	 BufferedWriter(Writer out) ：接收一个字符流对象（FileWriter或者OutputStreamWriter）
	 特有的方法：
	 newLine() ：实现换行的效果
	 特点：效率高速度快，适用于传输比较大的文件。

 字节流和字符流的区别
	 字节流和字符流的区别是：
	 字节流按 8 位二进制传输以字节为单位输入输出数据，
	 字符流按 16 位二进制传输以字符为单位输入输出数据。

	 读写单位不同：字节流以字节（8bit）为单位，字符流以字符为单位，根据码表映射字符，一次可能读多个字节。
	 处理对象不同：字节流能处理所有类型的数据（如图片、avi 等），而字符流只能处理字符类型的数据。
	 字节流：一次读入或读出是8 位二进制。
	 字符流：一次读入或读出是16 位二进制。

	 设备上的数据无论是图片或者视频，文字，它们都以二进制存储的。二进制的最终都是以一个8 位为数据单元进行体现，所以计算机中的最小数据单元就是字节。意味着，字节流可以处理设备上的所有数据，所以字节流一样可以处理字符数据。
	 结论：只要是处理纯文本数据，就优先考虑使用字符流。除此之外都使用字节流。

	 输入流只能进行读操作，输出流只能进行写操作，程序中需要根据待传输数据的不同特性而使用不同的流。

 */
public class Z04IOdemo {

	/**
	 * 古典问题：有一对兔子，从出生后第3个月起每个月都生一对兔子，
	 * 小兔子长到第三个月后每个月又生一对兔子，假如兔子都不死，
	 * 问每个月的兔子总数为多少？
	 * 这是一个菲波拉契数列问题
	 */
	@Test
	public void test01(){
		System.out.println("第1个月的兔子对数:    1");
		System.out.println("第2个月的兔子对数:    1");
		int f1 = 1, f2 = 1, f, M=24;
		for (int i = 3; i < M; i++) {
			 f = f2;
			 f2 = f1 + f2;
			 f1 = f;
			 System.out.println("第" + i +"个月的兔子对数: "+f2);
		}
	}

	/**
	 * 基于递归 计算阶乘 5!
	 */
	@Test
	public void test02(){
		int num=3;
		 System.out.println("利用递归计算"+num+"的阶乘为"//调用recursion
	                +recursion(num));

	}

	public static int recursion(int num){//利用递归计算阶乘
        int sum=1;
        if(num < 0)
            throw new IllegalArgumentException("必须为正整数!");//抛出不合理参数异常
        if(num==1){
            return 1;//根据条件,跳出循环
        }else{
            sum=num * recursion(num-1);//运用递归计算
            return sum;
        }
    }

	/*********************************************/

	/**
	 * 使用递归遍历 1- 100
	 */
	@Test
	public void test03(){
		test(100);
	}

	public static void test(int num){
		if(num >= 1){
			System.out.println(num);
			num--;
			test(num);      //调用自己的方法
		}
	}

	/*********************************************/
	/**
	 * 删除单级文件夹
	 * @throws IOException
	 */
	@Test
	public void test04() throws IOException{
		File dir = new File("C://Users//Administrator//Desktop//111");
		dir.mkdirs();//生成文件夹
		for (int j = 0; j < 5; j++) {
			File file = new File(dir,"文件"+j);
			file.createNewFile();//生成文件
			file.delete(); //删除文件
		}
		dir.delete(); //删除文件夹
	}

	/**
	 * 删除二级文件夹
	 * @throws IOException
	 */
	@Test
	public void test05() throws IOException {
		File dir = new File("G://123");
		dir.mkdirs();
		for (int i = 1; i <= 10; i++) {
			File file = new File(dir,"文件"+i);//在文件夹内添加文件，注意个数
			file.createNewFile();
		}
		File zidir = new File(dir,"子文件夹");//在父文件夹内生成子文件夹
		zidir.mkdirs();
		for (int i = 1; i <= 5; i++) {
			File zifile = new File(zidir,"子文件"+i);	//在子文件夹内生成子文件
			zifile.createNewFile();
		}
		//准备删除了  从父文件夹删除，做判断
		File[] listdir = dir.listFiles();
		for (File file1 : listdir) {
			if (file1.isFile()) {//判断，如果是文件，那么删除
				file1.delete();//将文件删除
			}else if (file1.isDirectory()) {//如果是目录，那么继续遍历
				File[] file2 = file1.listFiles();
				for (File file : file2) {
					file.delete();//将子文件删除
				}
				file1.delete();//将子文件夹删除
			}
		}
		dir.delete();//将父类文件夹删除
	}

	/**
	 *  遍历多级文件夹.
	 *  功能 : 给我一个文件夹,我能打印其下所有内容.
	 *  参数: File
	 *  返回值类型 : void
	 */
	@Test
	public void test06() {
		File dir = new File("G://123");
		printAllFile(dir);
	}

	public static void printAllFile(File dir) {
		if (dir.isFile()) {
			return;
		}
		if (!dir.exists()) {
			return;
		}
		// dir 就是目录.
		// 遍历 .
		File[] dirFiles = dir.listFiles();
		for (File file : dirFiles) {
			// file 是 dir 下的文件对象 .
			if (file.isFile()) {
				System.out.println("文件:" + file.getAbsolutePath());

			} else {
				// 文件夹
				// 调用自己.
				System.out.println("目录:" + file.getAbsolutePath());
				printAllFile(file);
			}
		}
	}

	/**
	 * 统计某文件夹下文件个数 jpg JPG png 格式
	 */
	public static int count = 0;  //静态count  不变 都可以用

	@Test
	public void test07() {
		File dir = new File("G:\\123");
		countFile(dir);
		System.out.println(count);
	}
	/*
	 * 参数: File dir 返回值类型: void
	 */
	public static void countFile(File dir) {
		// 文件
		if (dir.isFile()) {
			System.out.println("文件无法统计个数 ");
			return;
		}
		File[] listFiles = dir.listFiles();
		for (File f : listFiles) {
			// 判断
			if (f.isFile()) {
				// 获取名字
				String name = f.getName();
				if (name.endsWith("jpg") || name.endsWith("JPG") || name.endsWith("png")) {
					// 记数
					count++;
				}
			} else {
				// 文件夹了
				countFile(f);
			}
		}
	}

	/**
	 * 给定一个文件夹，递归打印这个文件夹下的所有.java文件的绝对路径
	 */
	@Test
	public void test08() {
		File file = new File("G:/123");
		changeJavaToText(file);
	}
	private static void changeJavaToText(File file) {
		//取出当前目录下所有的子级
		File[] sons = file.listFiles();
		//循环遍历所有子级
		for(File son : sons){
			//如果是文件夹
			if(son.isDirectory()){
				changeJavaToText(son); //递归 重新调用方法
			} else {
				String name = son.getAbsolutePath();//获取文件的路径下的文件名
				if(name.endsWith(".java")){ //判断
					System.out.println(name);
				}
			}
		}
	}

	/**
	 * 键盘录入一个路径，如果不是文件夹且是.java文件，那么更改为.txt文件
	 */
	@Test
	public void test09() {
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入一个文件路径：");
		String path = sc.nextLine();
		//封装为文件对象
		File file = new File(path);
		if(file.isDirectory()){
			System.out.println("这是文件夹");
		} else {
			String name = file.getAbsolutePath();  //新的路径
			//判断文件名
			if(name.endsWith(".java")){
				//新文件的名字
				String newName = name.replace(".java", ".txt");
				//创建新文件对象
				File newFile = new File(newName);
				boolean b = file.renameTo(newFile);
				System.out.println("修改成功？" + b);
			} else {
				System.out.println("不是.java文件");
			}
		}
	}

	/**
	 * 编写一个方法实现拷贝任意文件的功能，要求效率最高。
	 * @throws IOException
	 */
	@Test
	public void test10() throws IOException {
		System.out.print("请输入要拷贝的文件的绝对路径");
		Scanner s = new Scanner(System.in);
		File  inFile = new File(s.next());
		File outFile = new File("F:\\CopyFile.jpg");//拷贝到的路径
		System.out.println("拷贝成功，拷贝的路径是F:\\CopyFile.jpg");
		FileInputStream fileInputStream = new FileInputStream(inFile);
		FileOutputStream fileOutputStream = new FileOutputStream(outFile);
		BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
		BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
		int content = 0;
		while((content = bufferedInputStream.read())!=-1){
			bufferedOutputStream.write(content);
		}
		bufferedInputStream.close();
		bufferedOutputStream.close();
	}

	/**
	 * <dependency>
	 *     <groupId>commons-io</groupId>
	 *     <artifactId>commons-io</artifactId>
	 *     <version>2.6</version>
	 * </dependency>
	 *
	 * 常用方法：
	 * 	FilenameUtils:
	 * getExtension(String path)：获取文件的扩展名；
	 * getName()：获取文件名；
	 * isExtension(String fileName,String ext)：判断fileName是否是ext后缀名；
	 *
	 * FileUtils:
	 * readFileToString(File file)：读取文件内容，并返回一个String；
	 * writeStringToFile(File file，String content)：将内容写入到file中；
	 * copyDirectoryToDirectory(File srcDir,File destDir);文件夹复制
	 * copyFileToFile(File srcFile,File destFile);文件夹复制
	 */
	@Test
	public void test029(){
		String path = "C:\\Users\\root\\Pictures\\Saved Pictures\\12.jpg";
		//获取文件名
		String fileName = FilenameUtils.getName(path);
		System.out.println(fileName);
		//判断fileName是否是ext后缀名
		boolean jpg = FilenameUtils.isExtension(path, "jpg");
		System.out.println(jpg);
	}
}
